package com.et.machine.util

import android.util.Log
import com.et.drive.info.*
import com.et.drive.out.OutResult
import com.et.logger.TAGS
import com.et.machine.BuildConfig
import com.et.machine.convert.machineInfo
import com.et.machine.store.ChannelEntity
import com.et.util.endInsert2String
import com.et.util.headInsert2String
import com.et.util.md5
import com.et.util.toBCDInt
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import java.text.SimpleDateFormat
import java.util.*
import kotlin.experimental.and
import kotlin.experimental.inv
import kotlin.math.abs
import kotlin.math.ceil

private const val TCP_KEY = BuildConfig.tcp_key_mvv

object TcpTask {

    //51 ok
    suspend fun uploadSignIn(it: MachineInfo) = withContext(Dispatchers.IO) {
        StringBuilder(128).apply {
            append("CCC           ")
            append(BuildConfig.VERSION_NAME.headInsert2String(' ', 14))
            append(it.version)
            append(it.uuid)
            append(it.type)
            append(it.cDrink)
            append(it.cFood)
            append(it.cBox)
            append(it.nDrink.headInsert2String('0', 2))
            append(it.nFood.headInsert2String('0', 2))
            append(it.nBox.headInsert2String('0', 2))
            TcpUse.send2mv(it.uuid, this, "51")
        }
    }

    //62 ok
    suspend fun uploadChannelInfo(it: ChannelInfo) = withContext(Dispatchers.IO) {
        StringBuilder(512).apply {
            append(it.deviceNumber.headInsert2String('0', 2))
            it.data.copyOfRange(1, 7).forEach {
                val binary = Integer.toBinaryString(((it and 0xFF.toByte()).inv() + 0x100))
                append(binary.substring(binary.length - 8))
            }
            if (it.deviceNumber == 11) {
                it.data.copyOfRange(7, 55).forEach {
                    append((it.toInt() and 0xFF).toString(16).headInsert2String('0', 2))
                }
            } else {
                it.data.copyOfRange(7, 55).forEach {
                    append((it.toInt() and 0xFF).toString().headInsert2String('0', 2))
                }
            }
            Log.d("tcp_62", this.toString())
            Log.d("tcp_62", it.data.contentToString())
            TcpUse.send2mv(machineInfo!!.uuid, this, "62")
        }
    }

    //11 ok
    suspend fun uploadChannelStatus11(
        channelPrice: ChannelPrice,
        discountList: MutableList<String>,
        barcodeList: MutableList<String>
    ) = withContext(Dispatchers.IO) {
        StringBuilder(1024).apply {
            append(channelPrice.deviceNumber.headInsert2String('0', 2))
            for (i in 0 until barcodeList.size) {
                append(barcodeList[i].endInsert2String(' ', 16))
            }
            for (i in 0..47) {
                append(
                    (channelPrice.channelPrices.toBCDInt(i * 2, 2) * 10)
                        .headInsert2String('0', 6)
                )
            }
            for (i in 0 until discountList.size) {
                append(discountList[i].headInsert2String('0', 3))
            }
            Log.d("tcp_11", this.toString())
            TcpUse.send2mv(machineInfo!!.uuid, this, "11")
        }
    }

    //64 ok
    suspend fun uploadChannelStatus64(goodsID: ChannelGoodsID, channelPrice: ChannelPrice) =
        withContext(Dispatchers.IO) {
            StringBuilder(512).apply {
                append(goodsID.deviceNumber.headInsert2String('0', 2))
                for (i in 0..47) {
                    append(
                        (goodsID.status[i].toInt() and 0xFF).toString(16).headInsert2String('0', 2)
                    )
                }
                for (i in 0..47) {
                    append(
                        (channelPrice.channelPrices.toBCDInt(i * 2, 2) * 10).headInsert2String(
                            '0',
                            6
                        )
                    )
                }
                TcpUse.send2mv(machineInfo!!.uuid, this, "64")
            }
        }

    //12
    suspend fun uploadOutGoods(it: OutResult, order: String = "", barcode: String) =
        withContext(Dispatchers.IO) {
            StringBuilder(128).apply {
                append(it.device.headInsert2String('0', 2))
                append(it.channel.headInsert2String('0', 2))
                append("00")
                append(it.price.headInsert2String('0', 6))
                append(it.masterShipmentSerial)
                append(it.paymentMode)
                append(it.outResult)
                append(it.cardNumber.headInsert2String('0', 20))
                append(it.serial)
                append(it.totalQuantity.headInsert2String('0', 8))
                append(it.totalSale.headInsert2String('0', 10))
                append(
                    SimpleDateFormat(
                        "yyyyMMddHHmmss",
                        Locale.CHINA
                    ).format(Date(System.currentTimeMillis()))
                )
                append(order.endInsert2String(' ', 64))
                append(barcode.endInsert2String(' ', 16))
                append(0)

                TcpUse.send2mv(machineInfo!!.uuid, this, "12")
            }
        }

    //63 ok
    suspend fun uploadTmp(it: LightTemperatureInfo) = withContext(Dispatchers.IO) {
        StringBuilder(128).apply {
            append(it.deviceNumber.headInsert2String('0', 2))
            append(Integer.toBinaryString(it.salesConfig).headInsert2String('0', 8))
            append(
                when (it.lightStatus) {
                    LightStatusEnum.Auto -> "00"
                    LightStatusEnum.AlwaysClose -> "01"
                    LightStatusEnum.AlwaysOpen -> "02"
                }
            )
            append(it.openLightTime.startHour.headInsert2String('0', 2))
            append(it.openLightTime.startMin.headInsert2String('0', 2))
            append(it.openLightTime.endHour.headInsert2String('0', 2))
            append(it.openLightTime.endMin.headInsert2String('0', 2))
            append(
                when (it.leftRoomStatus) {
                    TemperatureStateEnum.Cool -> "00"
                    TemperatureStateEnum.Hot -> "01"
                    TemperatureStateEnum.Disable -> "02"
                }
            )
            append(
                when (it.rightRoomStatus) {
                    TemperatureStateEnum.Cool -> "00"
                    TemperatureStateEnum.Hot -> "01"
                    TemperatureStateEnum.Disable -> "02"
                }
            )
            append(it.energySavingTime.startHour.headInsert2String('0', 2))
            append(it.energySavingTime.startMin.headInsert2String('0', 2))
            append(it.energySavingTime.endHour.headInsert2String('0', 2))
            append(it.energySavingTime.endMin.headInsert2String('0', 2))

            append(
                if (it.settingCoolTemperature > 0) "+${
                    it.settingCoolTemperature.headInsert2String(
                        '0',
                        2
                    )
                }" else "-${abs(it.settingCoolTemperature).headInsert2String('0', 2)}"
            )
            append(
                if (it.actualCoolTemperature > 0) "+${
                    it.actualCoolTemperature.headInsert2String(
                        '0',
                        2
                    )
                }" else "-${abs(it.actualCoolTemperature).headInsert2String('0', 2)}"
            )

            append(
                if (it.settingHotTemperature > 0) "+${
                    it.settingHotTemperature.headInsert2String(
                        '0',
                        2
                    )
                }" else "-${abs(it.settingHotTemperature).headInsert2String('0', 2)}"
            )
            append(
                if (it.actualHotTemperature > 0) "+${
                    it.actualHotTemperature.headInsert2String(
                        '0',
                        2
                    )
                }" else "-${abs(it.actualHotTemperature).headInsert2String('0', 2)}"
            )

            TcpUse.send2mv(machineInfo!!.uuid, this, "63")
        }
    }

    //61 ok
    suspend fun uploadOperationInfo(machineOperationInfo: MachineOperationInfo) =
        withContext(Dispatchers.IO) {
            java.lang.StringBuilder(128).apply {
                append(machineOperationInfo.MachineRunStatus.isBusiness)
                append("00")
                append(if (machineOperationInfo.MachineRunStatus.isCloudBoardClosed) "1" else "0")
                append(if (machineOperationInfo.MachineRunStatus.isLightSenseClosed) "1" else "0")
                append(if (machineOperationInfo.MachineRunStatus.isGameOpened) "01" else "00")
                append(if (machineOperationInfo.MachineRunStatus.isGateOpened) "1" else "0")
                append(if (machineOperationInfo.MachineRunStatus.isNotAcceptedBanknotes) "1" else "0")

                append(if (machineOperationInfo.VendingMachineStatus.isNoResponseFromReader) "1" else "0")
                append(if (machineOperationInfo.VendingMachineStatus.isLightSensingFailure) "1" else "0")
                append(if (machineOperationInfo.VendingMachineStatus.isElectricLockDoorOpened) "1" else "0")
                append(if (machineOperationInfo.VendingMachineStatus.isNoGoodsToPickUp) "1" else "0")
                append(if (machineOperationInfo.VendingMachineStatus.isRightTemperatureSensorFault) "1" else "0")
                append(if (machineOperationInfo.VendingMachineStatus.isLeftTemperatureSensorFault) "1" else "0")
                append(if (machineOperationInfo.VendingMachineStatus.isCloudBoardStuck) "1" else "0")
                append(if (machineOperationInfo.VendingMachineStatus.isDriveNpResponse) "1" else "0")

                append(if (machineOperationInfo.BanknotesStatus.ignored) "1" else "0")
                append(if (machineOperationInfo.BanknotesStatus.isBanknoteHolderBlocked) "1" else "0")
                append(if (machineOperationInfo.BanknotesStatus.isMoneyBoxFull) "1" else "0")
                append(if (machineOperationInfo.BanknotesStatus.isMoneyBoxTakenAway) "1" else "0")
                append(if (machineOperationInfo.BanknotesStatus.isVerificationError) "1" else "0")
                append(if (machineOperationInfo.BanknotesStatus.isBanknotesSensorFault) "1" else "0")
                append(if (machineOperationInfo.BanknotesStatus.isMotorFailure) "1" else "0")
                append(if (machineOperationInfo.BanknotesStatus.isNoResponseFromBanknotesDevice) "1" else "0")

                append(if (machineOperationInfo.CoinsStatus.isOneYuanMisCurrency) "1" else "0")
                append(if (machineOperationInfo.CoinsStatus.isFiftyCentsMisCurrency) "1" else "0")
                append(if (machineOperationInfo.CoinsStatus.isBanknoteHolderBackBlocked) "1" else "0")
                append(if (machineOperationInfo.CoinsStatus.isBanknoteHolderInBlocked) "1" else "0")
                append(if (machineOperationInfo.CoinsStatus.isVerificationError) "1" else "0")
                append(if (machineOperationInfo.CoinsStatus.isCoinsSensorFault) "1" else "0")
                append(if (machineOperationInfo.CoinsStatus.isPowerLow) "1" else "0")
                append(if (machineOperationInfo.CoinsStatus.isNoResponseFromCoinsDevice) "1" else "0")

                append(machineOperationInfo.CoinsNumber.FiftyCentsNumber.headInsert2String('0', 4))
                append(machineOperationInfo.CoinsNumber.OneYuanNumber.headInsert2String('0', 4))
                append(machineOperationInfo.CoinsNumber.BanknotesNumber.headInsert2String('0', 4))

                TcpUse.send2mv(machineInfo!!.uuid, this, "61")
            }
        }

    //81
    suspend fun uploadMachineIDInfo(it: MachineIdInfo) = withContext(Dispatchers.IO) {
        StringBuilder(128).apply {
            append(it.MCCMIdentificationCode)
            append(it.nDrink2.headInsert2String('0', 2))
            append(it.nFood2.headInsert2String('0', 2))
            append(it.nFood3.headInsert2String('0', 2))
            append(it.nFood4.headInsert2String('0', 2))
            append(it.ignored)
            append(it.minDenomination?.headInsert2String('0', 3))
            append(it.decimalPlaces?.headInsert2String('0', 2))
            append(it.coinDenomination)
            append(it.notesDenomination)
            append(it.machineVersion?.headInsert2String('0', 6))

            TcpUse.send2mv(machineInfo!!.uuid, this, "81")
        }
    }

    //13 ok
    suspend fun uploadGoodsInfo(vararg channelEntity: ChannelEntity) = withContext(Dispatchers.IO) {
        var size = channelEntity.size
        for (sendCount in 0 until ceil(size / 28f).toInt()) {
            StringBuilder(1024).apply {
                val singleSize = if (size >= 28) 28 else size
                append(singleSize.headInsert2String('0', 3))
                for (index in sendCount * 28 until sendCount * 28 + singleSize) {
                    append(channelEntity[index].box.headInsert2String('0', 2))
                    append(channelEntity[index].channel.headInsert2String('0', 2))
                    append(channelEntity[index].barcode.endInsert2String(' ', 16))
                    append(channelEntity[index].priceCent.headInsert2String('0', 6))
                    append(channelEntity[index].discount.headInsert2String('0', 3))
                    append("00000")
                }
                TcpUse.send2mv(machineInfo!!.uuid, this, "13")
            }
            size -= 28
        }

    }

}


object TcpUse {

    fun send2mv(uuid: String, string: StringBuilder, code: String) {
        addSocketHead(uuid, string, code)
        TaskManager.instance?.apply {
            start()
            addTask(TcpBean(code, string.toString()))
        }

    }

    private fun addSocketHead(uuid: String, json: StringBuilder, controlCode: String) {
        Log.d(TAGS.machine, "addSocketHead")
        SimpleDateFormat("yyyyMMddHHmmss", Locale.CHINA).apply {
            val date: String = format(System.currentTimeMillis())
            val length = json.length + 37 - 4
            json.insert(0, controlCode)
            json.insert(0, 'J')
            json.insert(0, getSign(uuid, date).apply {
                Log.d(TAGS.machine, "TCP SIGN=$this")
            })
            json.insert(0, "17")
            json.insert(0, uuid.headInsert2String(len = 8))
            json.insert(0, date)
            json.insert(0, length.headInsert2String('0', 4))
        }
    }

    private fun getSign(uuid: String, time: String): String {
        val string0 = time + TCP_KEY + uuid
        val string1: String = md5(string0)
        val string2 = string1.substring(8) + string1.substring(0, 8)
        return "${string2[0]}${string2[3]}${string2[6]}${string2[1]}${string2[4]}${string2[7]}"
    }

}